#include <string>
#include <time.h>
#include <chrono>
#include <stdio.h>
#include <inttypes.h>
#include <stdint.h>

namespace zrpc {

class TimeStamp {
 public:
  TimeStamp() : micro_seconds_since_epoch_(0) {}

  explicit TimeStamp(int64_t micro_seconds_since_epoch_arg)
      : micro_seconds_since_epoch_(micro_seconds_since_epoch_arg) {}

  int64_t GetMicroSecondsSinceEpoch() const {
    return micro_seconds_since_epoch_;
  }

  time_t SecondsSinceEpoch() const {
    return static_cast<time_t>(micro_seconds_since_epoch_ /
                               kMicroSecondsPerSecond);
  }

  bool Valid() const { return micro_seconds_since_epoch_ > 0; }

  std::string ToFormattedString(bool showMicroseconds = true) const;

  static TimeStamp Now() {
    auto time_now = std::chrono::system_clock::now();
    auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(
        time_now.time_since_epoch());
    return TimeStamp(microseconds.count());
  }

  static uint64_t NowMicros() {
    auto time_now = std::chrono::system_clock::now();
    auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(
        time_now.time_since_epoch());
    return microseconds.count();
  }

  std::string ToString() const;
  static TimeStamp Invalid() { return TimeStamp(); }
  static const int32_t kMicroSecondsPerSecond = 1000 * 1000;

 private:
  int64_t micro_seconds_since_epoch_;
};

inline bool operator<(const TimeStamp &lhs, const TimeStamp &rhs) {
  return lhs.GetMicroSecondsSinceEpoch() < rhs.GetMicroSecondsSinceEpoch();
}

inline bool operator==(const TimeStamp &lhs, const TimeStamp &rhs) {
  return lhs.GetMicroSecondsSinceEpoch() == rhs.GetMicroSecondsSinceEpoch();
}

inline TimeStamp AddTime(const TimeStamp &timestamp, double seconds) {
  int64_t delta =
      static_cast<int64_t>(seconds * TimeStamp::kMicroSecondsPerSecond);
  return TimeStamp(timestamp.GetMicroSecondsSinceEpoch() + delta);
}

inline double TimeDifference(const TimeStamp &high, const TimeStamp &low) {
  int64_t diff =
      high.GetMicroSecondsSinceEpoch() - low.GetMicroSecondsSinceEpoch();
  return static_cast<double>(diff) / TimeStamp::kMicroSecondsPerSecond;
}

}  // namespace zrpc